Validate cross-referenced parameters between PostgresCluster and PostgresClusterClass#1858
Conversation
|
CLA Assistant Lite bot: I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request |
dbafebc to
fb47985
Compare
21145a6 to
74dca92
Compare
| wantMessage: "PostgresClusterClass not found", | ||
| }, | ||
| { | ||
| name: "rejected - storage below class floor", |
There was a problem hiding this comment.
storage below class floor - I assume we can override this value on a cluster lever even with lower ones, am I wrong?
| wantMessage: "storage cannot be lower than class default", | ||
| }, | ||
| { | ||
| name: "rejected - version below class floor", |
There was a problem hiding this comment.
version below class floor - same as above - can't we set lower versions on cluster level now?
There was a problem hiding this comment.
For the compliance we prohibit setting lower version than in class
| }, | ||
| }, | ||
| wantAllowed: false, | ||
| wantMessage: "connectionPoolerEnabled cannot be enabled", |
There was a problem hiding this comment.
"connectionPoolerEnabled cannot be enabled" - sounds strange (enabled cannot be enabled 😸 )
| wantMessage: "connectionPoolerEnabled cannot be enabled", | ||
| }, | ||
| { | ||
| name: "allowed - storage equal to class", |
There was a problem hiding this comment.
"allowed - storage equal to class" - and if higher?
| // ValidatePostgresClusterUpdate validates a PostgresCluster on UPDATE. | ||
| func ValidatePostgresClusterUpdate(obj, oldObj *enterpriseApi.PostgresCluster) field.ErrorList { | ||
| return ValidatePostgresClusterCreate(obj) | ||
| func ValidatePostgresClusterUpdate(obj, oldObj *enterpriseApi.PostgresCluster, reader client.Reader) field.ErrorList { |
There was a problem hiding this comment.
yes, we don't care about oldObj
| return allErrs | ||
| } | ||
|
|
||
| if obj.Spec.Storage != nil && classConfig.Storage != nil { |
There was a problem hiding this comment.
Same point for storage and postgresVersion: the merge code treats class values as defaults, but this webhook now treats them as minimum allowed values. If that policy shift is intentional, I’d update the API docs/comments because “overrides” reads differently today.
There was a problem hiding this comment.
I think there is no contradiction here i.e our default logic is there and it is used in the runtime reconciler but business rules validation is done here in the validation webhook. So we have "overrides" with guardrails in place. I agree we need to document this logic properly though
| class := &enterpriseApi.PostgresClusterClass{} | ||
| if err := reader.Get(context.Background(), client.ObjectKey{Name: obj.Spec.Class}, class); err != nil { | ||
| allErrs = append(allErrs, field.Invalid( | ||
| field.NewPath("spec").Child("class"), | ||
| obj.Spec.Class, | ||
| "referenced PostgresClusterClass not found")) | ||
| return allErrs | ||
| } |
There was a problem hiding this comment.
reader.Get() collapses every error into “referenced PostgresClusterClass not found”. That will hide real issues like API/RBAC failures and return a misleading validation error. I’d special-case IsNotFound and surface other errors separately.
| if k8sClient != nil || reader != nil { | ||
| vc = NewValidationContext(k8sClient, reader, req.Namespace) | ||
| } |
There was a problem hiding this comment.
Cross-resource validation now depends entirely on vc.Reader. If a caller provides only a cached client, or calls the validator directly, the checks silently disappear. I’d prefer a Reader -> Client fallback so the behavior is less wiring-sensitive.
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { |
There was a problem hiding this comment.
The new tests cover the happy path well, but they mostly assert error count/field or Allowed. I’d add message assertions for the unit tests and one update-path integration case for the cross-resource checks, so semantic regressions in the comparison logic are easier to catch.
| return b | ||
| } | ||
|
|
||
| func TestCrossResourceValidationIntegration(t *testing.T) { |
There was a problem hiding this comment.
The new tests cover the happy path well, but they mostly assert error count/field or Allowed. I’d add message assertions for the unit tests and one update-path integration case for the cross-resource checks, so semantic regressions in the comparison logic are easier to catch.
| ReadTimeout: readTimeout, | ||
| WriteTimeout: writeTimeout, | ||
| Client: mgr.GetClient(), | ||
| Reader: mgr.GetAPIReader(), |
There was a problem hiding this comment.
could you elab more, why we need this reader? What problem do we solve?
| } | ||
| } | ||
|
|
||
| if reader != nil { |
There was a problem hiding this comment.
could you elab why we expect some reader to validate against class logic?
| // Reader is a live API reader that bypasses the cache. | ||
| // Use this for cross-resource lookups where eventual consistency | ||
| // could cause false rejections. | ||
| Reader client.Reader |
There was a problem hiding this comment.
Is this really a problem in our case? if we for example deploy new cluster CR and we need to check latest deployed clusterClass isnt it already populated within the cache? Why we need to bypass it? Why this isnt a problem for other CR's checked by this?
62dfaba to
19b975f
Compare
19b975f to
96b28d3
Compare
Description
mgr.GetAPIReader()(live API read, not cached) to avoid false rejections during cache propagationKey Changes
spec.classspec.storagespec.postgresVersionspec.connectionPoolerEnabledtruewhen class has it disabledspec.instances,spec.postgresVersion,spec.storageTesting and Verification
client.Readercovering all rules and edge casesnilreader (no behavioral change)go build ./...passesgo test ./pkg/splunk/enterprise/validation/...passesRelated Issues
Jira tickets, GitHub issues, Support tickets...
PR Checklist